package com.hornsun.apply.service;

import com.google.common.collect.Maps;
import com.hornsun.apply.PayConfig;
import com.hornsun.domain.dbo.OrderForm;
import com.hornsun.repository.OrderFormRepository;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import static com.hornsun.apply.service.WXPayConstants.SignType.HMACSHA256;
import static com.hornsun.apply.service.WXPayUtil.HMACSHA256;

/**
 * <p>
 * 该文件的作用：//TODO
 * </p>
 *
 * @author xuxin
 * @date 2018/9/11
 */
@Service
public class ApplyService {

    @Autowired
    private PayConfig payConfig;

    @Autowired
    private WXPayRequest wxPayRequest;

    @Autowired
    private OrderFormRepository orderFormRepository;

    public Map<String, String> unifiedOrder(String orderGuid, String ip, String openId) throws Exception {
        OrderForm orderForm = orderFormRepository.getOrderByOrderGuid(orderGuid);
        Map<String, String> map = Maps.newHashMap();
        map.put("openid", openId);
        map.put("spbill_create_ip", ip);
        map.put("fee_type", payConfig.getFeeType());
        map.put("total_fee",orderForm.getActualExpenditure().toString());
        map.put("out_trade_no",orderForm.getAlias());
        map.put("appid", payConfig.getAppID());
        map.put("mch_id", payConfig.getMchID());
        map.put("notify_url","http://" + payConfig.getNotifyUrl() + "/api/order/paySuccess");
        map.put("nonce_str", WXPayUtil.generateNonceStr());
        map.put("sign_type", WXPayConstants.HMACSHA256);
        map.put("sign", generateSignature(map, payConfig.getKey()));
        String reqBody = WXPayUtil.mapToXml(map);
        String resp = wxPayRequest.requestWithoutCert(payConfig.getUndefinedUrlSuffix(),
                map.get("nonce_str"), reqBody,
                payConfig.getHttpConnectTimeoutMs(),
                payConfig.getHttpReadTimeoutMs(),
                true);
        return this.processResponseXml(resp);
    }

    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key  API密钥
     * @return 签名
     */
    private String generateSignature(final Map<String, String> data, String key) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            // 参数值为空，则不参与签名
            if (data.get(k).trim().length() > 0) {
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
            }
        }
        sb.append("key=").append(key);
        return HMACSHA256(sb.toString(), key);
    }

    /**
     * 处理 HTTPS API返回数据，转换成Map对象。return_code为SUCCESS时，验证签名。
     * @param xmlStr API返回的XML格式数据
     * @return Map类型数据
     * @throws Exception
     */
    public Map<String, String> processResponseXml(String xmlStr) throws Exception {
        String RETURN_CODE = "return_code";
        String return_code;
        Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
        if (respData.containsKey(RETURN_CODE)) {
            return_code = respData.get(RETURN_CODE);
        }
        else {
            throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
        }

        if (return_code.equals(WXPayConstants.FAIL)) {
            return respData;
        }
        else if (return_code.equals(WXPayConstants.SUCCESS)) {
            if (this.isResponseSignatureValid(respData)) {
                return respData;
            }
            else {
                throw new Exception(String.format("Invalid sign value in XML: %s", xmlStr));
            }
        }
        else {
            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
        }
    }


    /**
     * 判断xml数据的sign是否有效，必须包含sign字段，否则返回false。
     *
     * @param reqData 向wxpay post的请求数据
     * @return 签名是否有效
     * @throws Exception
     */
    public boolean isResponseSignatureValid(Map<String, String> reqData) throws Exception {
        // 返回数据的签名方式和请求中给定的签名方式是一致的
        return WXPayUtil.isSignatureValid(reqData, payConfig.getKey(), HMACSHA256);
    }

}



